﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using SharpAnalyzer.Entities;
using SharpAnalyzer.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace SharpAnalyzer.Collectors
{
    internal class MemberCollectorSyntaxWalker : CSharpSyntaxWalker
    {
        private readonly List<Member> members = new List<Member>();

        public MemberCollectorSyntaxWalker()
          : base(SyntaxWalkerDepth.Node)
        {
        }

        public IEnumerable<Member> GetMembers(SemanticModel semanticModel, CodeSectionDeclarationSyntaxInfo type)
        {
            Verify.NotNull(() => semanticModel);
            Verify.NotNull(() => type);

            Visit(type.Syntax);

            // Now try to set other properties of found members
            MemberNameResolver signatureResolver = new MemberNameResolver(semanticModel);
            foreach (Member member in members)
            {
                string signature;
                signatureResolver.TryResolveMemberSignatureString(member, out signature);
                member.DisplayName = signature;
                member.CodeFile = type.CodeFile;
                member.LineNumber = member.SyntaxNode.GetLineNumber();
            }
            return members.AsReadOnly();
        }

        public override void VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
        {
            base.VisitConstructorDeclaration(node);
            members.Add(new Member()
            {
                SyntaxNode = node,
                Kind = MemberKind.Constructor
            });
        }

        public override void VisitDestructorDeclaration(DestructorDeclarationSyntax node)
        {
            base.VisitDestructorDeclaration(node);
            members.Add(new Member()
            {
                SyntaxNode = node,
                Kind = MemberKind.Destructor
            });
        }

        public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
        {
            base.VisitMethodDeclaration(node);
            members.Add(new Member()
            {
                SyntaxNode = node,
                Kind = MemberKind.Method
            });
        }

        public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node)
        {
            base.VisitPropertyDeclaration(node);
            AddAccessorNode(node, node.AccessorList, SyntaxKind.GetAccessorDeclaration, MemberKind.GetProperty);
            AddAccessorNode(node, node.AccessorList, SyntaxKind.SetAccessorDeclaration, MemberKind.SetProperty);
        }

        public override void VisitEventDeclaration(EventDeclarationSyntax node)
        {
            base.VisitEventDeclaration(node);
            AddAccessorNode(node, node.AccessorList, SyntaxKind.AddAccessorDeclaration, MemberKind.AddEventHandler);
            AddAccessorNode(node, node.AccessorList, SyntaxKind.RemoveAccessorDeclaration, MemberKind.RemoveEventHandler);
        }

        private void AddAccessorNode(SyntaxNode node, AccessorListSyntax accessorList, SyntaxKind filter, MemberKind kind)
        {
            if (accessorList.Accessors.SingleOrDefault(x => x.Kind() == filter) == null)
                return;

            members.Add(new Member()
            {
                SyntaxNode = node,
                Kind = kind
            });
        }
    }
}
